# -*- coding: utf-8 -*-
# Copyright © 2024 weidongkl <weidongkx@gmail.com>
import logging
import time

from excavator.utils import reboot
from excavator.utils.output import ExcavatorPipelineOutput, ExcavatorGroupOutput, JobOutput
from excavator.utils.jsonutils import write, read
from excavator.version import get_version
from excavator.constants import NAME, DEFAULT_JSON_DATA
from excavator.job import get_group_jobs, sort_jobs, get_all_jobs
from excavator.group import BootGroup
from excavator.models.jobs import CheckJobsModel, UpgradeJobsModel, JobStatus
from excavator.exceptions import UnSupportPipeline

logger = logging.getLogger("excavator.pipeline")


class Runner(object):
    def __init__(self, groups, pipeline_name):
        self.func_output = JobOutput(indent=1)
        self.groups = groups
        self.pipeline_name = pipeline_name
        self._init_data()

    def _init_data(self):
        self.func_output.info(self.pipeline_name, "init excavator data")

        if self.pipeline_name == "check":
            self._write_json_data()
        try:
            read(DEFAULT_JSON_DATA)
        except Exception:
            self._write_json_data()

    def _write_json_data(self):
        data = {
            "name": NAME,
            "version": get_version()
        }
        write(DEFAULT_JSON_DATA, data)

    def run(self, *args, **kwargs):
        jobs = self.get()
        if jobs is None:
            _sort_jobs = self._sort()
            self.save(_sort_jobs)
            jobs = self.get()
        else:
            _sort_jobs = jobs.jobs
        _sort_job_cls = self.translete(_sort_jobs)

        last_job = None
        if self.pipeline_name == "upgrade":
            check_job_obj = CheckJobsModel.get()
            if check_job_obj.status != JobStatus.success:
                logger.warning("check job failed, please solve it")
                self.func_output.info(self.pipeline_name, "check job failed, please solve it")
                self.func_output.info(self.pipeline_name, "check report in (/var/lib/excavator/check_report.txt)")

                exit(1)
            else:
                if jobs.status == JobStatus.prepare:
                    _sort_job_cls = _sort_job_cls[len(check_job_obj.jobs):]
                elif jobs.status == JobStatus.running:
                    self.func_output.info(self.pipeline_name, "continue upgrade")
                    current_job = jobs.current_job
                    index = jobs.jobs.index(current_job)
                    _sort_job_cls = _sort_job_cls[index + 1:]
                else:
                    logger.info("upgrade completed")
                    exit(0)

        for job in _sort_job_cls:
            if self.pipeline_name == "upgrade":
                if job["group"] == BootGroup.name:
                    if last_job is not None and last_job["group"] != BootGroup.name:
                        if kwargs.get("yes", False):
                            msg = "auto reboot to perform the upgrade after 0.5 seconds"
                            logger.info(msg)
                            self.func_output.info(self.pipeline_name, msg)
                            time.sleep(0.5)
                            reboot()
                            exit(0)
                        else:
                            msg = "use \"reboot\" command to perform the upgrade"
                            logger.info(msg)
                            self.func_output.info(self.pipeline_name, msg)
                            exit(0)
            if last_job is None or last_job["group"] != job["group"]:
                ExcavatorGroupOutput.info(job["group"], "start running {} group jobs".format(job["group"]))
            self.update(job['job'])
            job['job']().run(args, kwargs)
            last_job = job

    def save(self, save_sort_jobs):
        if self.pipeline_name == "check":
            logger.info(save_sort_jobs)
            return CheckJobsModel.create(jobs=save_sort_jobs, current_job="", status=JobStatus.prepare)
        elif self.pipeline_name == "upgrade":
            return UpgradeJobsModel.create(jobs=save_sort_jobs, current_job="", status=JobStatus.prepare)
        else:
            raise UnSupportPipeline()

    def update(self, job):

        if self.pipeline_name == "check":
            obj = CheckJobsModel.get()
        elif self.pipeline_name == "upgrade":
            obj = UpgradeJobsModel.get()
        else:
            raise UnSupportPipeline()
        if obj.status == JobStatus.prepare:
            obj.update(current_job=job.name, status=JobStatus.running)
        else:
            obj.update(current_job=job.name)

    def translete(self, jobs):
        """传入模型列表，返回job对象列表"""
        _sort_job_cls = []
        all_jobs = get_all_jobs()
        # TODO: 这个对比应该可以优化，现在是复杂度最高的写法，考虑到job数有限，实际影响不大
        for job in jobs:
            for job_cls in all_jobs:
                if job == job_cls.name:
                    _sort_job_cls.append({
                        "group": job_cls.group.name,
                        "job": job_cls
                    })
                    break
        return _sort_job_cls

    def get(self):
        if self.pipeline_name == "check":
            return CheckJobsModel.get()
        elif self.pipeline_name == "upgrade":
            return UpgradeJobsModel.get()
        else:
            raise UnSupportPipeline()

    def _sort(self):
        self.func_output.info(self.pipeline_name, "sort jobs")
        _sort_jobs = []
        _sort_jobs_cls = []
        # 排序应当包含之前的jobs产生的数据
        for group in self.groups:
            group_jobs = get_group_jobs(group)
            group_jobs_index = len(_sort_jobs_cls)
            _sort_jobs_cls.extend(group_jobs)
            _sort_jobs_cls = _sort_jobs_cls[:group_jobs_index] + sort_jobs(_sort_jobs_cls)[group_jobs_index:]

        _sort_jobs = [job.name for job in _sort_jobs_cls]
        return _sort_jobs
